/*
 * Created on March 14, 2011, 6:48 PM
 *
 *  @file:      rnaffe_application_server.h
 *  @details    Header file for RnaFFEApplicationServer class.\n
 *              System: CombEng \n
 *              Language: C++\n
 *
 *  @author     Favio Bettiol
 *  @email      favio.bettiol AT gmail.com
 *
 *  @author     Diego Diaz
 *  @email      diazdiego86 AT gmail.com
 *
 *  @date       August 2010
 *  @version    0.1
 *
 * This file is part of CombEng
 *
 * CombEng : Combinatory Engine, an FuD's abstraction layer for problems that
 * require to combine elements. Also, this layer provides ranking and prune
 * features for these combinations.
 * <http://fud.googlecode.com/>
 *
 * Copyright (C) 2010 - Favio Bettiol and Diego Diaz
 *
 * CombEng is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CombEng is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CombEng .  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef RNAFFE_APPLICATION_SERVER_H
#define RNAFFE_APPLICATION_SERVER_H

#include <cassert>
#include <sstream>
#include <fstream>
#include <mili/mili.h>
#include "rnaffe_node.h"

using namespace comb_eng;
class RnaFFEApplicationServer: public comb_eng::L5ApplicationServer<lav::ResisBasedAntiviral>
{

private:
    RnaFFENode* _root_node;
    std::ofstream output_file;

public:

    class ScoredTherapy
    {
    private:
        static biopp::NucDistance sum_dist(const DistancesList& dl)
        {
            size_t accum = 0;
            for (DistancesList::const_iterator it = dl.begin(); it != dl.end(); ++it)
                accum += *it;

            return accum;
        }

    public:
        const DistancesList distances;
        const Therapy therapy_ids;
        const size_t distances_sum;

        ScoredTherapy(const DistancesList& dl, const Therapy& ids) : distances(dl),
            distances_sum(sum_dist(dl)), therapy_ids(ids)
        {
            assert(distances.size() - 1 == therapy_ids.size());
        }

        void print() const
        {
            std::cout << "distances sum: " << distances_sum << "\n";
            Therapy::const_iterator it = therapy_ids.begin();
            std::list<size_t>::const_iterator itt;
            DistancesList::const_iterator d_it = distances.begin();
            while (it != therapy_ids.end())
            {
                std::cout << " |";
                itt = (*it).begin();
                while (itt != (*it).end())
                {
                    std::cout << all_antivirals.get_name_by_id(*itt) << "|";
                    ++itt;
                }
                ++d_it;
                std::cout << " <" << *d_it << ">";
                ++it;
            }
            std::cout << std::endl;
        }
    };

    struct TherapyGreater
    {
        bool operator()(const ScoredTherapy& t1, const ScoredTherapy& t2) const
        {
            return t1.distances_sum > t2.distances_sum;
        }
    };

    struct TherapyLess
    {
        bool operator()(const ScoredTherapy& t1, const ScoredTherapy& t2) const
        {
            return t1.distances_sum < t2.distances_sum;
        }
    };
    
    
    
    struct TherapyOrder
    {
        static signed int compareDoses(const Dose& d1, const Dose& d2)
        {
           
            const size_t size1 = d1.size();                      
            const size_t size2 = d2.size();
            signed int ret;
 
            if ( size1 < size2 )
                ret = -1;
            else if ( size1 > size2 )
                ret = 1;
            else // equal sizes
            {
                // d1.size >= d2.size, therefore I'll iterate until d2.end which is the smallest
                unsigned int accum1 = 0;
                unsigned int accum2 = 0;
                for ( Dose::const_iterator it1 = d1.begin(), it2 = d2.begin(); it2 != d2.end(); ++it1, ++it2)
                {
                    accum1 |= 1 << *it1;
                    accum2 |= 1 << *it2;
                }
                ret = static_cast<signed int>(accum1) - static_cast<signed int>(accum2);
            }
            return ret;
        }
 
   
        bool operator()(const ScoredTherapy& t1, const ScoredTherapy& t2) const
        {
            const size_t size1 = t1.therapy_ids.size();                      
            const size_t size2 = t2.therapy_ids.size();
            bool isLess;
 
            if ( size1 < size2 )
                isLess = true;
            else if ( size1 > size2 )
                isLess = false;
            else // equal sizes
            {
                // t1.size == t2.size
                Therapy::const_iterator it1 = t1.therapy_ids.begin();
                Therapy::const_iterator it2 = t2.therapy_ids.begin();
                signed int comparison = 0;
                while (comparison == 0 && it2 != t2.therapy_ids.end())
                {
                    comparison = compareDoses (*it1, *it2);
                    ++it1; ++it2;
                }
                isLess = (comparison < 0);
            }
            return isLess;
        }
 
    };

    typedef mili::UniqueRanker<ScoredTherapy, TherapyGreater, TherapyOrder> TopRanking;
    TopRanking top_ranker;

    typedef mili::UniqueRanker<ScoredTherapy, TherapyLess, TherapyOrder> BottomRanking;
    BottomRanking bottom_ranker;



    void print_top_ranker () const ;


    /**
    *  Construtor method.
    *  @param seq: Path file containing the initial sequence.
    *  @param db: Path file containing all availables antivirals.
    *  @param res_file: Path file where results will be stored.
    *  @param fold: Pointer to a fold type. It will be used to get the folding free energy.
    */
    RnaFFEApplicationServer(const char* seq, const char* res_file, const IFold* fold);

    /**
    *  Destrutor method.
    */
    ~RnaFFEApplicationServer();

    /**
    *  Method to get a packet representing the application root node.
    *  @param pkt: Here should be stored the root-node serialization.
    */
    void get_initial_packet(recabs::Packet& pkt) const;

    /**
    *  Describes what should be do when a result arrives to the server.
    *  @param pkt: It represents the result. It has to be deserialized.
    */
    void receive_result(const recabs::Packet& pkt);

};
#endif  /* RNAFFE_APPLICATION_SERVER_H */

